#include <assert.h>
#include <time.h>
#include <iostream>
#include "fecudp/message_encode_buffer.h"
#include "fecudp/message_decode_buffer.h"

#pragma comment(lib, "ws2_32.lib")

MessageList simulat_loss_and_decode(const FecPacketList& sendpacket, int data_num, int parity_num)
{
    MessageDecodeBuffer decode_buferr(1);
    MessageList msgs;
    for (auto& packet : sendpacket)
    {
        msgs = 
            decode_buferr.appendAndTryDecode(packet->get_packet_payload(), packet->get_packet_length());
        if (msgs.size() > 0)
            break;
    }

    return msgs;
}

bool one_msg_encode_and_decode(const std::string& testmsg, int data_num, int parity_num)
{
    MessageEncodeBuffer encode_buferr(data_num, parity_num);

    FecPacketList sendpacket = encode_buferr.appendAndTryEncode(testmsg.c_str(), testmsg.size());
    MessageList  msgs = simulat_loss_and_decode(sendpacket, data_num, parity_num);

    if (msgs.size() != 1)
        return false;

    std::string recvmsg = *(msgs.begin());
    bool result = (0 == memcmp(recvmsg.c_str(), testmsg.c_str(), recvmsg.size()))
        && (recvmsg.size() == testmsg.size());
    return result;
}

void Test_OneMsg_Encode_and_Decode()
{
    {
        std::string testmsg(20*1024, 't');
        assert(one_msg_encode_and_decode(testmsg, 8, 8));
        assert(one_msg_encode_and_decode(testmsg, 8, 1));
        assert(one_msg_encode_and_decode(testmsg, 1, 1));
        assert(one_msg_encode_and_decode(testmsg, 1, 2));
        assert(one_msg_encode_and_decode(testmsg, 2, 2));
        assert(one_msg_encode_and_decode(testmsg, 1, 8));
    }

    {
        std::string testmsg1(1, 't');
        assert(!one_msg_encode_and_decode(testmsg1, 2, 1));
        assert(one_msg_encode_and_decode(testmsg1, 1, 2));
        assert(one_msg_encode_and_decode(testmsg1, 1, 1));
    }

    {
        std::string testmsg2(UDP_EFC_PACKET_BODY_MAX_SIZE, 't');
        assert(one_msg_encode_and_decode(testmsg2, 1, 2));
        assert(!one_msg_encode_and_decode(testmsg2, 2, 1));
    }

    {
        std::string testmsg3(UDP_EFC_PACKET_BODY_MAX_SIZE+1, 't');
        assert(one_msg_encode_and_decode(testmsg3, 1, 2));
        assert(one_msg_encode_and_decode(testmsg3, 2, 1));
    }

    {
        std::string testmsg4(UDP_EFC_PACKET_BODY_MAX_SIZE-1, 't');
        assert(one_msg_encode_and_decode(testmsg4, 1, 2));
        assert(!one_msg_encode_and_decode(testmsg4, 2, 1));
    }
}

void Test_MultiMsg_Encode_and_Decode()
{
    int msg_num = 10;
    int data_num = 8;
    int parity_num = 4;
    srand((unsigned)time(NULL));
    std::vector<std::string> send_msg_list;
    for (int i = 0; i < msg_num; ++i)
    {
        if (i<msg_num/2)
            send_msg_list.push_back(std::string(1000 + rand() % 8000, 'a' + i));
        else
            send_msg_list.push_back(std::string(rand() % 100, 'a' + i));
    }

    //encode and create send packet
    MessageEncodeBuffer encode_buferr(data_num, parity_num);
    FecPacketList sendpacket;
    for (int i = 0; i < msg_num; ++i)
    {
        FecPacketList temp = encode_buferr.appendAndTryEncode(send_msg_list[i].c_str(), send_msg_list[i].size());
        sendpacket.insert(sendpacket.end(), temp.begin(), temp.end());
    }
    FecPacketList temp = encode_buferr.forceEncode();
    sendpacket.insert(sendpacket.end(), temp.begin(), temp.end());

    //
    MessageDecodeBuffer decode_buferr(1);
    std::vector<std::string> recv_msg_list;
    size_t i = 0;
    for (auto& packet : sendpacket)
    {
        if (i++ % (data_num+parity_num) == 0)
            continue;

        MessageList tmp = 
            decode_buferr.appendAndTryDecode(packet ->get_packet_payload(), packet ->get_packet_length());
        recv_msg_list.insert(recv_msg_list.end(), tmp.begin(), tmp.end());
    }

    std::cout << send_msg_list.size() << std::endl;
    std::cout << recv_msg_list.size() << std::endl;
    assert(send_msg_list.size() == recv_msg_list.size());

    for (int i = 0; i < send_msg_list.size(); ++i)
    {
        assert(send_msg_list[i].size() == recv_msg_list[i].size());
        assert(0 == memcmp(send_msg_list[i].c_str(), recv_msg_list[i].c_str(), recv_msg_list[i].size()));
    }
}

void Test_MessageDecodeBuffer_windown_size()
{
    //todo: 
}

void Test_MessageEncodeBuffer_clear()
{
    {
        int parity_num = 8;
        int data_num = 8;
        MessageEncodeBuffer encode_buferr(8, 8);
        MessageDecodeBuffer decode_buferr(1);
        std::string testmsg(1000, 'a');
        std::string testmsg1(1000, 'b');
        std::string testmsg2(1000, 'c');
        std::string testmsg3(15000, 'd');
        FecPacketList sendpacket;

        sendpacket = encode_buferr.appendAndTryEncode(testmsg.c_str(), testmsg.size());
        assert(sendpacket.size() == 0);
        sendpacket = encode_buferr.appendAndTryEncode(testmsg1.c_str(), testmsg1.size());
        assert(sendpacket.size() == 0);
        sendpacket = encode_buferr.appendAndTryEncode(testmsg2.c_str(), testmsg2.size());
        assert(sendpacket.size() == 0);
        encode_buferr.clear();

        sendpacket = encode_buferr.appendAndTryEncode(testmsg3.c_str(), testmsg3.size());
        assert(sendpacket.size() > 0);

        MessageList msgs = simulat_loss_and_decode(sendpacket, data_num, parity_num);
        assert(msgs.size() == 1);
        std::string recvmsg = *(msgs.begin());
        bool result = (0 == memcmp(recvmsg.c_str(), testmsg3.c_str(), recvmsg.size()))
            && (recvmsg.size() == testmsg3.size());
    }
}

void Test_MessageEncodeBuffer_forceEncode()
{
    {
        int parity_num = 8;
        int data_num = 8;
        MessageEncodeBuffer encode_buferr(8, 8);
        MessageDecodeBuffer decode_buferr(1);
        std::string testmsg(4000, 'a');

        FecPacketList sendpacket;
        sendpacket = encode_buferr.appendAndTryEncode(testmsg.c_str(), testmsg.size());
        assert(sendpacket.size() == 0);
        sendpacket = encode_buferr.forceEncode();
        assert(sendpacket.size() > 0);
        FecPacketList tmp = encode_buferr.forceEncode();
        assert(tmp.size() == 0);

        MessageList msgs = simulat_loss_and_decode(sendpacket, data_num, parity_num);
        assert(msgs.size() == 1);
        std::string recvmsg = *(msgs.begin());
        bool result = (0 == memcmp(recvmsg.c_str(), testmsg.c_str(), recvmsg.size()))
            && (recvmsg.size() == testmsg.size());
    }
}

void Test_MessageEncodeBuffer_empty()
{
    {
        int parity_num = 8;
        int data_num = 8;
        MessageEncodeBuffer encode_buferr(8, 8);
        MessageDecodeBuffer decode_buferr(1);
        std::string testmsg(6000, 'a');
        assert(encode_buferr.empty());
        FecPacketList sendpacket;
        sendpacket = encode_buferr.appendAndTryEncode(testmsg.c_str(), testmsg.size());
        assert(!encode_buferr.empty());
        sendpacket = encode_buferr.appendAndTryEncode(testmsg.c_str(), testmsg.size());
        assert(encode_buferr.empty());
    }
}

void Test_MultiMsg_Encode_and_Decode(int send_msg_num)
{
    MessageEncodeBuffer encode_buferr(4, 1);
    MessageDecodeBuffer decode_buferr(2);
    FecPacketList sendps;
    int recv_msg_num = 0;
    for (int i = 0; i < send_msg_num; ++i)
    {
        std::string msg(i, 'a' + i%26);
        sendps = encode_buferr.appendAndTryEncode(msg.c_str(), msg.size());
        if (sendps.empty())
            continue;

        int lost = 0;
        for (auto& p : sendps)
        {
            if (lost++ == 0)
                continue;

            MessageList msgs = decode_buferr.appendAndTryDecode(p->get_packet_payload(), p->get_packet_length());
            if (!msgs.empty())
            {
                for (auto& msg : msgs)
                {
                    recv_msg_num++;
                    std::string tmp(msg.size(), msg[0]);
                    assert(tmp.size() == msg.size());
                    assert(memcmp(&tmp[0], &msg[0], msg.size()) == 0);
                }
            }
        }
    }

    assert(send_msg_num == recv_msg_num);
}

int main(int argc, char* argv[])
{
    Test_OneMsg_Encode_and_Decode();
    Test_MultiMsg_Encode_and_Decode();
    Test_MultiMsg_Encode_and_Decode(10000);
    Test_MessageDecodeBuffer_windown_size();

    Test_MessageEncodeBuffer_clear();
    Test_MessageEncodeBuffer_forceEncode();
    Test_MessageEncodeBuffer_empty();



    return 0;
}